package cn.vivi.seckill.service.impl;

import cn.vivi.seckill.pojo.SecKillMessage;
import cn.vivi.seckill.pojo.SeckillGoods;
import cn.vivi.seckill.pojo.SeckillOrder;
import cn.vivi.seckill.pojo.User;
import cn.vivi.seckill.rabbitmq.MQSender;
import cn.vivi.seckill.service.SecKillGoodsService;
import cn.vivi.seckill.service.SecKillOrderService;
import cn.vivi.seckill.service.SecKillService;
import cn.vivi.seckill.util.JsonUtils;
import cn.vivi.seckill.util.MD5Utils;
import cn.vivi.seckill.util.UUIDUtils;
import cn.vivi.seckill.vo.RespBean;
import cn.vivi.seckill.vo.RespBeanEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;


@Service
@Slf4j
public class SecKillServiceImpl implements SecKillService, InitializingBean {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private SecKillGoodsService secKillGoodsService;

    @Autowired
    private SecKillOrderService secKillOrderService;

    @Autowired
    private MQSender mqSender;

    // 空库存的 map 集合
    private Map<Long, Boolean> emptyStockMap = new HashMap<>();

    @Override
    public RespBean isToSecKill(String goodsId, String userTicket) {
        User user = (User) redisTemplate.opsForValue().get("user:" + userTicket);
        if (Objects.isNull(user)) {
            // 没有登陆
            return RespBean.error(RespBeanEnum.USER_NO_LOGIN);
        }

        // 重复抢购
        SeckillOrder seckillOrder = (SeckillOrder) redisTemplate.opsForValue().get("order:" + user.getPhone() + ":" + goodsId);
        if (!Objects.isNull(seckillOrder)) {
            return RespBean.error(RespBeanEnum.REPEAT_TO_SNAP_UP);
        }

        // 内存标记，减少 Redis 的访问
        if (emptyStockMap.get(Long.parseLong(goodsId))) {
            // 库存为空
            return RespBean.error(RespBeanEnum.STOCK_NO_ENOUGH);
        }

        ValueOperations valueOperations = redisTemplate.opsForValue();
        // 预减库存
        Long stock = valueOperations.decrement("seckillGoods:" + goodsId);

        // 库存不足
        if (stock < 0) {
            emptyStockMap.put(Long.parseLong(goodsId), true);
            valueOperations.increment("seckillGoods:" + goodsId);
            return RespBean.error(RespBeanEnum.STOCK_NO_ENOUGH);
        }

        // 秒杀消息
        SecKillMessage message = new SecKillMessage(user, goodsId);
        mqSender.sendSecKillMessage(JsonUtils.toJson(message));
        // TODO 发送消息到 MQ 之后的事情
        return RespBean.success(RespBeanEnum.SEND_MESSAGE_SUCCESS);
    }

    /**
     * 查询秒杀结果
     */
    @Override
    public RespBean getResult(String goodsId, String userTicket) {
        User user = (User) redisTemplate.opsForValue().get("user:" + userTicket);
        if (Objects.isNull(user)) {
            return RespBean.error(RespBeanEnum.USER_NO_LOGIN);
        }
        SeckillOrder secKillOrder = secKillOrderService.findSecKillOrder(goodsId, user.getPhone());
        if (!Objects.isNull(secKillOrder)) {
            return RespBean.success(secKillOrder.getOrderId());
        } else if (redisTemplate.hasKey("isStockEmpty:" + goodsId)) {
            // 下单失败，库存不足
            return RespBean.error(RespBeanEnum.STOCK_NO_ENOUGH);
        } else {
            // 排队中
            return RespBean.error(RespBeanEnum.STAND_IN_LINE);
        }
    }

    /**
     * 创建秒杀地址
     */
    @Override
    public RespBean createPath(String userTicket, String goodsId) {
        User user = (User) redisTemplate.opsForValue().get("user:" + userTicket);
        if (Objects.isNull(user)) {
            return RespBean.error(RespBeanEnum.USER_NO_LOGIN);
        }
        String path = MD5Utils.md5(UUIDUtils.uuid() + "123456");
        redisTemplate.opsForValue().set("seckillPath:" + user.getPhone() + ":" + goodsId, path, 60, TimeUnit.SECONDS);
        return RespBean.success(path);
    }

    /**
     * 验证路径是否合法
     */
    @Override
    public boolean checkPath(String path, String userTicket, String goodsId) {
        User user = (User) redisTemplate.opsForValue().get("user:" + userTicket);
        if (Objects.isNull(user) || Integer.parseInt(goodsId) < 0 || StringUtils.isEmpty(path)) {
            return false;
        }
        String redisPath = (String) redisTemplate.opsForValue().get("seckillPath:" + user.getPhone() + ":" + goodsId);

        return path.equals(redisPath);
    }

    /**
     * 初始化秒杀商品数量到 redis 中
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        List<SeckillGoods> goods = secKillGoodsService.findAllSecKillGoods();
        if (CollectionUtils.isEmpty(goods)) {
            return;
        }

        goods.forEach(g -> {
            redisTemplate.opsForValue().set("seckillGoods:" + g.getGoodsId(), g.getStockCount());
            emptyStockMap.put(g.getGoodsId(), false);
        });

    }

}
